package com.angel.data.generator.interfaces.impl;

import java.io.InputStream;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.List;

import org.apache.log4j.Logger;

import com.angel.architecture.exceptions.NonBusinessException;
import com.angel.architecture.flex.locator.ObjectLocator;
import com.angel.architecture.initializer.injector.DependecyInjector;
import com.angel.architecture.initializer.modules.DataGeneratorModuleDescriptor;
import com.angel.common.helpers.ReflectionHelper;
import com.angel.dao.generic.factory.DAOFactory;
import com.angel.dao.generic.interfaces.GenericDAO;
import com.angel.data.generator.annotations.Generator;
import com.angel.data.generator.annotations.importFileProcessorRunner.ImportFileProcessorRunnerBuilder;
import com.angel.data.generator.annotations.inputStream.InputStreamBuilder;
import com.angel.data.generator.exceptions.DataGeneratorException;
import com.angel.data.generator.interfaces.DataGenerator;
import com.angel.io.descriptor.FileProcessorDescriptor;
import com.angel.io.exceptions.InvalidRowDataException;
import com.angel.io.processors.runners.imports.impl.ImportFileProcessorRunner;

/**
 *
 * @author William
 *
 */
public class AnnotationDataGenerator extends ImportFileDataGenerator {

	private static final Logger LOGGER = Logger.getLogger(AnnotationDataGenerator.class);
	private Object generatorObject;
	private Method imporFileProcessorRunnerBuilderMethod;
	private Method inputStreamBuilderMethod;

	public <T> AnnotationDataGenerator(Object generatorObject, Method imporFileProcessorRunnerBuilderMethod, Method inputStreamBuilderMethod){
		super();
		this.generatorObject = generatorObject;
		this.setImporFileProcessorRunnerBuilderMethod(imporFileProcessorRunnerBuilderMethod);
		this.setInputStreamBuilderMethod(inputStreamBuilderMethod);
		Class<?> classGenerator = generatorObject.getClass();
		Generator generatorAnnotation = classGenerator.getAnnotation(Generator.class);
		this.setPages(this.buildPagesArray(generatorAnnotation.pages()));
	}

	protected Integer[] buildPagesArray(int[] pagesPrimitive){
		Integer[] pages = null;
		if(pages == null){
			pages = new Integer[]{0};
		} else {
			pages = new Integer[pagesPrimitive.length];
			int i = 0;
			for(int page : pagesPrimitive){
				pages[i] = page;
				i++;
			}
		}
		return pages;
	}

	public void generateData(Collection<Object> dataCollection, DAOFactory daoFactory) {
		try {
			LOGGER.info("Preparing input stream.");
			InputStream inputStream = this.prepareInputStream();
			LOGGER.info("Preparing import file processor runner.");
			ImportFileProcessorRunner importFileProcessorRunner = this.prepareImportFileProcessorRunner();
			LOGGER.info("Begin run process for [" + this.getStringPages() + "] pages.");
			List<Object> entities = importFileProcessorRunner.runProcess(inputStream, this.getPages());
			LOGGER.info("End run process. It process [" + entities.size() + "] entities.");
			dataCollection.addAll(entities);
		} catch (InvalidRowDataException e) {
			throw new DataGeneratorException("An unexpected error ocurred running import process.", e);
		} catch (Exception e) {
			throw new DataGeneratorException("An unexpected error ocurred running import process.", e);
		}
	}

	public Class<? extends Object> getDataGeneratorClass() {
		return this.getGenerator().objectClass();
	}

	@SuppressWarnings("unchecked")
	public Class<? extends DataGenerator>[] getDependecesGenerators() {
		return (Class<? extends DataGenerator>[]) this.getGenerator().dependencies();
	}

	public GenericDAO<Object, Serializable> getGenericDAO() {
		//TODO Verify it adds genericDAO at super class, and inject this object at moment when object creates.
		return ObjectLocator.getBeanByName(this.getGenerator().daoName());
	}

	protected Generator getGenerator(){
		return this.getGeneratorObject().getClass().getAnnotation(Generator.class);
	}
	/**
	 * @return the generatorObject
	 */
	public Object getGeneratorObject() {
		return generatorObject;
	}

	/**
	 * @param generatorObject the generatorObject to set
	 */
	public void setGeneratorObject(Object generatorObject) {
		this.generatorObject = generatorObject;
	}

	/**
	 * @return the imporFileProcessorRunnerBuilderMethod
	 */
	protected Method getImporFileProcessorRunnerBuilderMethod() {
		return imporFileProcessorRunnerBuilderMethod;
	}

	/**
	 * @param imporFileProcessorRunnerBuilderMethod the imporFileProcessorRunnerBuilderMethod to set
	 */
	protected void setImporFileProcessorRunnerBuilderMethod(
			Method imporFileProcessorRunnerBuilderMethod) {
		this.imporFileProcessorRunnerBuilderMethod = imporFileProcessorRunnerBuilderMethod;
	}

	/**
	 * @return the inputStreamBuilderMethod
	 */
	protected Method getInputStreamBuilderMethod() {
		return inputStreamBuilderMethod;
	}

	/**
	 * @param inputStreamBuilderMethod the inputStreamBuilderMethod to set
	 */
	protected void setInputStreamBuilderMethod(Method inputStreamBuilderMethod) {
		this.inputStreamBuilderMethod = inputStreamBuilderMethod;
	}

	@Override
	public ImportFileProcessorRunner prepareImportFileProcessorRunner() {
		ImportFileProcessorRunnerBuilder builder = this.getImporFileProcessorRunnerBuilderMethod().getAnnotation(ImportFileProcessorRunnerBuilder.class);
		if(builder != null){
			FileProcessorDescriptor fileProcessorDescriptor = (FileProcessorDescriptor) ReflectionHelper.createObject(builder.fileProcessorDescriptor());
			fileProcessorDescriptor.setColumnSeparator(builder.columnSeparator());
			fileProcessorDescriptor.setName(builder.name());
			fileProcessorDescriptor.setHasHeader(builder.hasHeader());
			ImportFileProcessorRunner importFileProcessorRunner = null;
			try {
				importFileProcessorRunner = (ImportFileProcessorRunner)
					/*ReflectionHelper.invokeMethodWithParameters(
							this.getGeneratorObject(),
							this.getImporFileProcessorRunnerBuilderMethod(),
							new Object[]{fileProcessorDescriptor}
						);*/
				this.getImporFileProcessorRunnerBuilderMethod().invoke(this.getGeneratorObject(), new Object[]{fileProcessorDescriptor});
			} catch (IllegalArgumentException e) {
				throw new NonBusinessException("Error occured builind impor file processor runner at [" + this.getImporFileProcessorRunnerBuilderMethod().getName() + "] at class [" + this.getImporFileProcessorRunnerBuilderMethod().getDeclaringClass() + "].", e);
			} catch (IllegalAccessException e) {
				throw new NonBusinessException("Error occured builind impor file processor runner at [" + this.getImporFileProcessorRunnerBuilderMethod().getName() + "] at class [" + this.getImporFileProcessorRunnerBuilderMethod().getDeclaringClass() + "].", e);
			} catch (InvocationTargetException e) {
				throw new NonBusinessException("Error occured builind impor file processor runner at [" + this.getImporFileProcessorRunnerBuilderMethod().getName() + "] at class [" + this.getImporFileProcessorRunnerBuilderMethod().getDeclaringClass() + "].", e);
			}
	    	DependecyInjector a = new DependecyInjector();
	    	a.autoInject(importFileProcessorRunner.getImportRowCommand(), new DataGeneratorModuleDescriptor());
			return importFileProcessorRunner;
		}
		throw new NonBusinessException("Method [" + this.getImporFileProcessorRunnerBuilderMethod().getName() + "] has not ImportFileProcessorRunnerBuilder annotation.");
	}

	@Override
	public InputStream prepareInputStream() {
		InputStreamBuilder inputStreamBuilder = this.getInputStreamBuilderMethod().getAnnotation(InputStreamBuilder.class);
		if(inputStreamBuilder != null){
			return (InputStream) ReflectionHelper.invokeMethodWithoutParameters(this.getGeneratorObject(), this.getInputStreamBuilderMethod());
		}
		throw new NonBusinessException("Method [" + this.getInputStreamBuilderMethod().getName() + "] has not ImportFileProcessorRunnerBuilder annotation.");
	}

}

